package com.liuya.safe.user.service.impl;

import java.io.StringReader;
import java.util.*;

import com.liuya.base.AbstractBaseService;
import com.liuya.common.SecurityUtil;
import com.liuya.common.exception.EntityExistException;
import com.liuya.common.StringUtil;
import com.liuya.db.DBLevelException;
import com.liuya.db.MapStorageObjectNewer;
import com.liuya.db.MapStorgeColumnAdapter;
import com.liuya.db.MapStorgeObject;

import com.liuya.db.orm.Column;
import com.liuya.db.orm.Table;
import com.liuya.db.sql.BinaryExpression;
import com.liuya.db.sql.ExpressionGroup;
import com.liuya.db.sql.Operator;
import com.liuya.db.sql.Query;
import com.liuya.db.sql.QueryFactory;
import com.liuya.db.sql.SimpleOperator;
import com.liuya.db.sql.SimpleValue;
import com.liuya.safe.client.pojo.FieldMetadata;
import com.liuya.safe.client.pojo.UserType;
import com.liuya.safe.model.SafeUser;
import com.liuya.safe.user.service.UserComplexTableAdapter;
import com.liuya.safe.user.service.UserManagerService;
import org.springframework.stereotype.Service;

@Service("userManagerService")
public class UserManagerServiceImpl extends AbstractBaseService<SafeUser> implements UserManagerService {
    private UserType userType;
    private UserComplexTableAdapter adapter;
    // The fields which would be shown on user panel
    private Collection shownFields = new ArrayList();


    public UserManagerServiceImpl() {
    }

    public UserManagerServiceImpl(UserType userType) {
        init(userType);
    }

    private void init(UserType userType){
        this.userType = userType;
        adapter = new UserComplexTableAdapter(userType.getUserMetadata());

        // shownFields
        String[] shownFieldNames = userType.getUserMetadata().getShowUserFields();
        FieldMetadata[] fields = userType.getUserMetadata().getMainTableMetadata().getFields();
        for (int i = 0; i < fields.length; i++) {
            for (int j = 0; j < shownFieldNames.length; j++) {
                if (fields[i].getName().equals(shownFieldNames[j])) {
                    shownFields.add(fields[i]);
                }
            }
        }
    }

    private Query adapterQuery(Table t) {
        StringBuffer b = new StringBuffer();

        b.append("<query name=\"innserQueryUser\" ds=\"" + t.getDatasourceName() + "\">");
        b.append("<select>");
        Column[] columns = t.getColumns();
        for (int i = 0; i < columns.length; i++) {
            Column column = columns[i];
            String javaType = "java.lang.Object";
            MapStorgeColumnAdapter adapter = (MapStorgeColumnAdapter) column.getAdapter();
            javaType = adapter.getClassName();
            b.append("<column name=\"" + column.getName() + "\" tableAlias=\"u\" javaType=\"" + javaType + "\"/>");
        }
        b.append("</select>");
        b.append("<from>");
        b.append("<table schema=\"\" name=\"" + t.getName() + "\" alias=\"u\"/>");
        b.append("</from>");
        b.append("<rawSQL><content/></rawSQL>");
        b.append("</query>");

        com.liuya.db.sql.xml.QueryType xmlQuery;
        try {
            xmlQuery = com.liuya.db.sql.xml.Query.unmarshal(new StringReader(b.toString()));
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
        com.liuya.db.sql.Query sqlQuery = QueryFactory.getQuery(xmlQuery);

        return sqlQuery;
    }

    private void enforce(Query query) {
        query.getSelect().setMappingClass(null);
        query.getSelect().setObjectNewer(new MapStorageObjectNewer());

        // set column adapter
        ArrayList columns = query.getSelect().getColumns();
        int count = columns.size();
        for (int i = 0; i < count; i++) {
            com.liuya.db.sql.Column column = (com.liuya.db.sql.Column) columns.get(i);
            column.setAdapter(new MapStorgeColumnAdapter(column.getProperty(), column.getJavaType()));
        }
    }

    /**
     * Adapter sqlQuery to a new Query, add LIKE %key% to where condition
     *
     * @param mainTable
     * @param key
     *            search value
     * @param refQuery
     * @return
     */
    private Query adapterQuery(Table mainTable, String key, Query refQuery) {
        Query sqlQuery = null;
        if (refQuery != null) {
            sqlQuery = refQuery;
            enforce(sqlQuery);
        } else {
            sqlQuery = adapterQuery(mainTable);
        }

        ExpressionGroup expressionGroupForKey = getExpressionGroupForKey(key, sqlQuery);

        if (expressionGroupForKey != null && hasExpression(sqlQuery)) {
            // TODO change this impl later, put orginal express at first like
            // expression group back,
            ExpressionGroup expressionGroup = new ExpressionGroup();
            expressionGroup.setLinker(ExpressionGroup.AND);
            expressionGroup.getExpressions().add(expressionGroupForKey);
            expressionGroup.getExpressions().add(sqlQuery.getWhere().getExpressionGroup());
            sqlQuery.getWhere().setExpressionGroup(expressionGroup);
        } else if (expressionGroupForKey != null && !hasExpression(refQuery)) {
            sqlQuery.getWhere().setExpressionGroup(expressionGroupForKey);
        }

        sqlQuery.reloadSQL();
        return sqlQuery;
    }

    private boolean hasExpression(Query query) {
        if (query == null) {
            return false;
        }
        return query.getWhere().getExpressionGroup().getExpressions().size() > 0;
    }

    private ExpressionGroup getExpressionGroupForKey(String key, Query sqlQuery) {
        if (StringUtil.isEmpty(key)) {
            return null;
        }

        ExpressionGroup expressionGroup = new ExpressionGroup();
        expressionGroup.setLinker(ExpressionGroup.OR);
        Iterator columnItr = sqlQuery.getSelect().getColumns().iterator();
        while (columnItr.hasNext()) {
            com.liuya.db.sql.Column column = (com.liuya.db.sql.Column) columnItr.next();
            if (!isColumnShown(column)) {
                continue;
            }

            BinaryExpression binaryExpression = new BinaryExpression();
            // operand1
            binaryExpression.setOperand1(column);
            // operand2
            SimpleValue operand2 = new SimpleValue();
            operand2.setType(SimpleValue.STRING);
            operand2.setValue(key);
            binaryExpression.setOperand2(operand2);
            // LIKE operator
            Operator operator = new SimpleOperator();
            operator.setValue(SimpleOperator.LIKE);
            binaryExpression.setOperator(operator);

            expressionGroup.getExpressions().add(binaryExpression);

            // TODO change this impl later, put orginal express at first like
            // expression group back,
            // add simple value to values
            sqlQuery.getValues().add(0, operand2);
        }

        return expressionGroup;
    }

//    public int selectUserCounts(SafeUser user, Map context, String key, Query refQuery) {
//        Map param = new HashMap();
//        List result = mapper.selectAll(param);
//        return result.size();
//    }

//    public Collection selectUsers(SafeUser user, Map context, String key, Query refQuery, int start, int limit) {
//        Map param = new HashMap();
//        param.put("limit", " limit " + start + "," + limit);
//        List result = mapper.selectAll(param);
//        return result;
//    }

//    public Collection selectUsersByCustomizedJavaBean(SafeUser user, Map context, String key, Query refQuery, int start, int limit) {
//        Query sqlQuery = adapterQuery(adapter.getComplexTable().getMainTable(), key, refQuery);
//
//        Collection data = sqlQuery.execute(user, context, start, limit).getData();
//        List result = new ArrayList(data.size());
//        for (Iterator iter = data.iterator(); iter.hasNext();) {
//            MapStorgeObject mso = (MapStorgeObject) iter.next();
//            result.add(adapter.javabeanToUser(mso));
//        }
//        return result;
//    }

    private boolean isColumnShown(com.liuya.db.sql.Column column) {
        Iterator itr = shownFields.iterator();
        while (itr.hasNext()) {
            FieldMetadata field = (FieldMetadata) itr.next();
            if (field.getColumnName().equalsIgnoreCase(column.getName())) {
                return true;
            }
        }
        return false;
    }

    public void save(SafeUser user) throws DBLevelException, EntityExistException {
//        MapStorgeObject mso = adapter.toMSO(user);
//        saver.save(mso);
        mapper.insert(user);
    }

    public SafeUser update(SafeUser user) {
//        MapStorgeObject mso = adapter.toMSO(user);
//        updator.update(mso);
        mapper.updateByPrimaryKey(user);
        return null;
    }

    public void delete(SafeUser user) {
        // delete this user's role-user relationship
//        Collection applications = com.liuya.Factory.getUserTypeManager().getApplications(userType.getName());
//        Iterator itr = applications.iterator();
//        while (itr.hasNext()) {
//            Application application = (Application) itr.next();
//            UserRoleManager userRoleManager = Factory.getUserRoleManager(application.getName(), userType.getName());
//            userRoleManager.deleteUserRoles(user.get(SafeUser.idFieldName));
//        }

        // delete user
//        MapStorgeObject mso = adapter.toMSO(user);
//        deletor.delete(mso);
        mapper.deleteByPrimaryKey(user.getId());
    }

    public SafeUser selectById(String id) {
//        MapStorgeObject mso = adapter.toMSO(user);
//        MapStorgeObject selectValue = (MapStorgeObject) selector.selectByIdColumns(mso);
//        return adapter.toUser(selectValue);
        return mapper.selectByPrimaryKey(id);
    }

    public SafeUser selectBy(SafeUser user){
        Map params = new HashMap();
        params.put("username", user.getName());
        //对用户密码进行加密，数据库不存明文
        user.setPassword(SecurityUtil.encryptMd5(user.getPassword()));
        params.put("password", user.getPassword());
        try{
            List<SafeUser> lists = mapper.selectAll(params);
            if(lists.size()>0)
                return lists.get(0);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    public SafeUser selectByName(String name) {
        Map params = new HashMap();
        params.put("name", name);
        List user = mapper.selectAll(params);
        if(user!=null && user.size()>0){
            return (SafeUser)user.get(0);
        } else {
            return null;
        }
    }

//    public SafeUser selectByUniqueFields(SafeUser user) {
////        MapStorgeObject mso = adapter.toMSO(user);
////        MapStorgeObject selectValue = (MapStorgeObject) selector.selectByUniqueColumns(mso);
////        return adapter.toUser(selectValue);
//        return null;
//    }

    public Collection selectAll() {
        return mapper.selectAll(null);
    }
}
